package cn.com.utils;

import com.github.dockerjava.api.DockerClient;
import com.github.dockerjava.api.command.BuildImageCmd;
import com.github.dockerjava.api.command.BuildImageResultCallback;
import com.github.dockerjava.api.model.AuthConfig;
import com.github.dockerjava.api.model.BuildResponseItem;
import com.github.dockerjava.api.model.Container;
import com.github.dockerjava.api.model.Image;
import com.github.dockerjava.core.DockerClientBuilder;
import com.github.dockerjava.core.command.PullImageResultCallback;
import com.github.dockerjava.core.command.PushImageResultCallback;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.util.List;
import java.util.Set;

/**
 * 这个类用于操作Docker镜像和容器。 它提供了标记、保存、加载、构建、删除、列出镜像和容器以及提取镜像的方法。 它使用Docker Java API与Docker守护进程进行通信。
 * <p>
 * Docker Java API是Docker的Java客户端库。它允许您构建和运行Docker容器 以及Java应用程序中的图像。
 *
 * @author LiuGuodong
 * @date 2024/06/03
 */
public class DockerUtil {

    private DockerClient dockerClient;

    private AuthConfig authConfig;

    /**
     * 构造函数，用于初始化Docker客户端
     *
     * @param dockerHost Docker守护程序的主机地址
     */
    public DockerUtil(String dockerHost) {
        this.dockerClient = DockerClientBuilder.getInstance(dockerHost).build();
    }

    /**
     * 构造函数，用于初始化Docker客户端和身份验证信息
     *
     * @param dockerHost Docker守护程序的主机地址
     * @param username 仓库用户名
     * @param password 仓库密码
     * @param email 仓库邮箱
     * @param registryAddress 仓库地址
     */
    public DockerUtil(String dockerHost, String username, String password, String email, String registryAddress) {
        this.dockerClient = DockerClientBuilder.getInstance(dockerHost).build();
        this.authConfig = new AuthConfig()
                .withUsername(username)
                .withPassword(password)
                .withEmail(email)
                .withRegistryAddress(registryAddress);
    }

    /**
     * 给镜像打标签
     *
     * @param imageId 镜像ID
     * @param repository 镜像仓库
     * @param tag 标签
     */
    public void tagImage(String imageId, String repository, String tag) {
        dockerClient.tagImageCmd(imageId, repository, tag).exec();
    }

    /**
     * 导出容器
     *
     * @param containerId 容器ID
     * @param exportPath 导出路径
     *
     * @throws Exception 异常
     */
    public void exportContainer(String containerId, String exportPath) throws Exception {
        try (InputStream response = dockerClient.execStartCmd(containerId).getStdin();
             FileOutputStream outputStream = new FileOutputStream(new File(exportPath))) {
            byte[] buffer = new byte[4096];
            int bytesRead;
            while ((bytesRead = response.read(buffer)) != -1) {
                outputStream.write(buffer, 0, bytesRead);
            }
        }
    }

    /**
     * 保存镜像
     *
     * @param imageId 镜像ID
     * @param savePath 保存路径
     *
     * @throws Exception 异常
     */
    public void saveImage(String imageId, String savePath) throws Exception {

        File file = new File(savePath);
        if (!file.exists()) {
            // 创建目标路径
            file.getParentFile().mkdirs();
        }

        try (InputStream response = dockerClient.saveImageCmd(imageId).exec();
             FileOutputStream outputStream = new FileOutputStream(file)) {

            byte[] buffer = new byte[4096];
            int bytesRead;
            while ((bytesRead = response.read(buffer)) != -1) {
                outputStream.write(buffer, 0, bytesRead);
            }

        }
    }

    /**
     * 加载镜像
     *
     * @param loadPath 加载路径
     *
     * @throws Exception 异常
     */
    public void loadImage(String loadPath) throws Exception {
        try (InputStream inputStream = new FileInputStream(new File(loadPath))) {
            dockerClient.loadImageCmd(inputStream).exec();
        }
    }

    /**
     * 构建镜像
     *
     * @param dockerfilePath Dockerfile路径
     * @param tag 镜像标签
     *
     * @return 构建的镜像ID
     */
    public String buildImage(String dockerfilePath, String tag) {
        BuildImageCmd buildImageCmd = dockerClient.buildImageCmd(new File(dockerfilePath));
        BuildImageResultCallback buildImageResultCallback = new BuildImageResultCallback() {
            @Override
            public void onNext(BuildResponseItem item) {
                super.onNext(item);
            }
        };

        String imageId = buildImageCmd.exec(buildImageResultCallback).awaitImageId();
        tagImage(imageId, "", tag);
        return imageId;
    }

    /**
     * 构建镜像
     *
     * @param dockerfilePath dockerfile路径
     * @param tagName        标记名称
     * @param version        版本
     * @return {@link String }
     */
    public String buildImage(String dockerfilePath, String tagName, String version) {
        if (dockerfilePath == null || version == null || version.isEmpty()) {
            throw new IllegalArgumentException("Dockerfile路径和标签不能为空");
        }

        File dockerfile = new File(dockerfilePath);
        if (!dockerfile.exists() || !dockerfile.isFile()) {
            throw new IllegalArgumentException("无效的Dockerfile路径");
        }

        BuildImageCmd buildImageCmd = dockerClient.buildImageCmd(dockerfile);
        BuildImageResultCallback buildImageResultCallback = new BuildImageResultCallback() {
            @Override
            public void onNext(BuildResponseItem item) {
                super.onNext(item);
                // 处理构建日志或错误信息
                if (item.getStream() != null) {
                    System.out.println("构建日志：" + item.getStream());
                }
            }
        };

        String imageId = buildImageCmd.exec(buildImageResultCallback).awaitImageId();
        tagImage(imageId, tagName, version);
        return imageId;
    }


    /**
     * 构建镜像
     *
     * @param inputStream 输入流，包含Dockerfile和上下文内容
     * @param tag         镜像标签
     * @return 构建的镜像ID
     */
    public String buildImageFromStream(InputStream inputStream, String tag) {
        try {
            // 创建一个回调以处理构建输出
            BuildImageResultCallback callback = new BuildImageResultCallback() {
                @Override
                public void onNext(BuildResponseItem item) {
                    super.onNext(item);
                }
            };

            // 开始构建镜像
            dockerClient.buildImageCmd(inputStream)
                    .exec(callback);

            return "imageId";

        } catch (Exception e) {
            // 处理异常，例如构建失败
            return null;
        }
    }

    /**
     * 构建镜像
     *
     * @param contextDirectory 上下文目录路径
     * @param tag 镜像标签
     *
     * @return 构建的镜像ID
     */
    public String buildImageFromContext(String contextDirectory, String tag) {
        return dockerClient.buildImageCmd(new File(contextDirectory))
                           .withTags(Set.of(tag))
                           .start()
                           .awaitImageId();
    }


    /**
     * 推送镜像到Docker仓库
     *
     * @param repository 镜像仓库地址，例如 swr.cn-north-4
     * @param tag 标签
     *
     * @throws InterruptedException 异常
     */
    public void pushImage(String repository, String tag) throws InterruptedException {
        String imageFullName = repository + "/" + tag;
        dockerClient.pushImageCmd(imageFullName)
                    .withAuthConfig(authConfig)
                    .exec(new PushImageResultCallback())
                    .awaitCompletion();
    }

    /**
     * 删除镜像
     *
     * @param imageId 镜像ID
     */
    public void removeImage(String imageId) {
        dockerClient.removeImageCmd(imageId).withForce(true).exec();
    }

    /**
     * 列出容器
     *
     * @return 容器列表
     */
    public List<Container> listContainers() {
        return dockerClient.listContainersCmd().exec();
    }

    /**
     * 列出镜像
     *
     * @return 镜像列表
     */
    public List<Image> listImages() {
        return dockerClient.listImagesCmd().exec();
    }

    /**
     * 拉取镜像
     *
     * @param repository 镜像仓库
     * @param tag 标签
     *
     * @throws InterruptedException 异常
     */
    public void pullImage(String repository, String tag) throws InterruptedException {
        if (authConfig != null) {
            dockerClient.pullImageCmd(repository)
                        .withTag(tag)
                        .withAuthConfig(authConfig)
                        .exec(new PullImageResultCallback())
                        .awaitCompletion();
        } else {
            dockerClient.pullImageCmd(repository)
                        .withTag(tag)
                        .exec(new PullImageResultCallback())
                        .awaitCompletion();
        }
    }

    /**
     * 登录到仓库
     *
     * @param username 仓库用户名
     * @param password 仓库密码
     * @param email 仓库邮箱
     * @param registryAddress 仓库地址
     */
    public void login(String username, String password, String email, String registryAddress) {
        this.authConfig = new AuthConfig()
                .withUsername(username)
                .withPassword(password)
                .withEmail(email)
                .withRegistryAddress(registryAddress);
    }

    /**
     * 登录到Docker仓库
     *
     * @param username 用户名，格式为 cn-north-4@MSLN8MX130IZ46RT62ID
     * @param password 密码，例如 f1ed89d2542756a51b3be7aac870f0848ce57e463585d0c3e1a87fcf9f4f9053
     * @param registryAddress Docker仓库地址，例如 swr.cn-north-4
     */
    public void login(String username, String password, String registryAddress) {
        this.authConfig = new AuthConfig()
                .withUsername(username)
                .withPassword(password)
                .withRegistryAddress(registryAddress);
        dockerClient.authCmd().withAuthConfig(authConfig).exec();
    }
}
